#include<cstdint>
#include <atomic>

namespace nsbtree {
class RWLock {
private:
    uint8_t lock : 1;  // exclusive bits for 
    uint8_t reader_num : 7;

public:
    RWLock() {
        lock = 0;
        reader_num = 0;
    }

    void RLock() { // atomically add the reader_num
		RWLock expected = *this;
		RWLock desired;

        bool success;
        do {
            // expected = *this;
            expected.lock = 0;   // if locked, wait
            desired.reader_num = expected.reader_num + 1; // add one
            success = __atomic_compare_exchange(this, &expected, &desired, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE);
        } while(success == false);
    }

    void WLock() { // atomically set the intent and lock bit, make sure reader_num == 0
		RWLock expected;
        RWLock desired;
		desired.lock = 1;   // desire to be locked
        expected.reader_num = this->reader_num;
    
        bool success;
        do {
            // expected = *this;
            expected.lock = 0;       // if locked, wait
            desired.reader_num = expected.reader_num; // do not update reader_num
            success = __atomic_compare_exchange(this, &expected, &desired, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE);
        } while(success == false);

        RWLock tmp;
        do {
            __atomic_load(this, &tmp, __ATOMIC_ACQUIRE);
        } while(tmp.reader_num > 0);
    }

    void UnRLock() { // atomically sub the reader_num
		RWLock expected = *this;
        RWLock desired;  // all zero

        bool success;
        do {
            // expected = *this;
            desired.lock = expected.lock; // locked or not, whatever
            desired.reader_num = expected.reader_num - 1; // sub one
            success = __atomic_compare_exchange(this, &expected, &desired, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE);
        } while(success == false);
    }

    void UnWLock() { // clear all bits
        RWLock desired;  // all zero
        RWLock old;
        __atomic_exchange(this, &desired, &old, __ATOMIC_ACQUIRE);
    }

    void Clear() {
        lock = 0;
        reader_num = 0;
    }
};

}